#include "BasicBlock.hpp"
#include "Constant.hpp"
#include "Function.hpp"
#include "IRBuilder.hpp"
#include "Module.hpp"
#include "Type.hpp"

#include <iostream>
#include <memory>

// 定义一个从常数值获取/创建 ConstantInt 类实例化的宏，方便多次调用
#define CONST_INT(num) \
    ConstantInt::get(num, module)

// 定义一个从常数值获取/创建 ConstantFP 类实例化的宏，方便多次调用
#define CONST_FP(num) \
    ConstantFP::get(num, module)

int main() {
    // 创建一个 Module 实例，Module 代表一个完整的编译单元，包含多个函数和全局变量等信息
    auto module = new Module();
    // 创建一个 IRBuilder 实例，用于在模块中构建 LLVM IR 指令
    auto builder = new IRBuilder(nullptr, module);
    // 从 Module 中获取 32 位整数类型的实例
    Type *Int32Type = module->get_int32_type();

    // 创建 main 函数，函数返回类型为 32 位整数，无参数
    auto mainFun = Function::create(FunctionType::get(Int32Type, {}), "main", module);
    // 创建 main 函数的入口基本块，基本块是一系列顺序执行的指令，且只有一个入口和一个出口
    auto entryBB = BasicBlock::create(module, "entry", mainFun);
    // 将 IRBuilder 的插入点设置为入口基本块，后续创建的指令将插入到该基本块中
    builder->set_insert_point(entryBB);

    // 在栈上为 main 函数的返回值分配空间
    auto retAlloca = builder->create_alloca(Int32Type);
    // 初始化返回值为 0
    builder->create_store(CONST_INT(0), retAlloca);

    // 在栈上为变量 a 分配空间
    auto aAlloca = builder->create_alloca(Int32Type);
    // 在栈上为变量 i 分配空间
    auto iAlloca = builder->create_alloca(Int32Type);

    // 初始化变量 a 的值为 10
    builder->create_store(CONST_INT(10), aAlloca);
    // 初始化变量 i 的值为 0
    builder->create_store(CONST_INT(0), iAlloca);

    // 创建循环条件判断的基本块
    auto loopCondBB = BasicBlock::create(module, "loop_cond", mainFun);
    // 创建循环体的基本块
    auto loopBodyBB = BasicBlock::create(module, "loop_body", mainFun);
    // 创建循环结束后的基本块
    auto afterLoopBB = BasicBlock::create(module, "after_loop", mainFun);

    // 从入口基本块跳转到循环条件判断基本块
    builder->create_br(loopCondBB);

    // 将 IRBuilder 的插入点设置为循环条件判断基本块
    builder->set_insert_point(loopCondBB);
    // 从栈上加载变量 i 的值
    auto iLoad = builder->create_load(iAlloca);
    // 比较变量 i 的值是否小于 10，生成比较结果
    auto cmp = builder->create_icmp_lt(iLoad, CONST_INT(10));
    // 根据比较结果进行条件跳转，如果 i < 10 则跳转到循环体基本块，否则跳转到循环结束后的基本块
    builder->create_cond_br(cmp, loopBodyBB, afterLoopBB);

    // 将 IRBuilder 的插入点设置为循环体基本块
    builder->set_insert_point(loopBodyBB);
    // 再次从栈上加载变量 i 的值
    auto iLoad2 = builder->create_load(iAlloca);
    // 计算 i + 1 的结果
    auto iInc = builder->create_iadd(iLoad2, CONST_INT(1));
    // 将计算结果存储回变量 i 的栈空间
    builder->create_store(iInc, iAlloca);
    // 从栈上加载变量 a 的值
    auto aLoad = builder->create_load(aAlloca);
    // 再次从栈上加载变量 i 的值
    auto iLoad3 = builder->create_load(iAlloca);
    // 计算 a + i 的结果
    auto aAdd = builder->create_iadd(aLoad, iLoad3);
    // 将计算结果存储回变量 a 的栈空间
    builder->create_store(aAdd, aAlloca);
    // 从循环体基本块跳转到循环条件判断基本块，继续下一次循环判断
    builder->create_br(loopCondBB);

    // 将 IRBuilder 的插入点设置为循环结束后的基本块
    builder->set_insert_point(afterLoopBB);
    // 从栈上加载变量 a 的最终值
    auto aLoadFinal = builder->create_load(aAlloca);
    // 将变量 a 的最终值作为 main 函数的返回值
    builder->create_ret(aLoadFinal);

    // 输出模块中生成的所有 LLVM IR 指令
    std::cout << module->print();

    // 释放 Module 实例占用的内存
    delete module;
    // 释放 IRBuilder 实例占用的内存
    delete builder;

    return 0;
}
